[Terraform]Amazon ECRのコンテナイメージをAWS CodePipeline経由でAmazon ECSにデプロイする際のポイント

[Terraform]Amazon ECRのコンテナイメージをAWS CodePipeline経由でAmazon ECSにデプロイする際のポイント

Amazon ECRのコンテナイメージをAWS CodePipeline経由でAmazon ECSにデプロイする際に、いくつかハマったポイントがありました。それらの解決方法をまとめたので、Terraformコードと一緒に紹介します。
Clock Icon2024.07.29

コンテナイメージをデプロイしたい

おのやんです。

みなさん、Amazon ECR(以下、ECR)のコンテナイメージをAmazon ECS(以下、ECS)にデプロイしたいとおもったことはありませんか?私はあります。

AWSでは、AWS CodePipeline(以下、CodePipeline)を設定することで、AWSサービス内でCI/CDのパイプラインを設定できます。CodePipelineは、マネジメントコンソール上で設定する分には、AWS側でいい感じにサジェストしてくれるので、比較的簡単に設定することができます。しかしTerraformで設定する際にはCodePipeline側の挙動をドキュメントと照らし合わせて、適切な設定を記述する必要があります。

今回、ECRにpushしたコンテナイメージをECRへデプロイするCodePipelineのパイプラインを、Terraformで記述する機会がありましたので、この際のポイントをまとめていきたいと思います。

今回の構成

今回目指していた構成は以下の画像のようになります。ECRには、nginxのコンテナイメージをpushしておきます。このイメージに対して、CodePipelineを設定してECRへデプロイします。また、デプロイ設定は、S3にアップロードしたimagedefinitions.jsonの圧縮ファイルをCodePipelineに参照させています。

architecture

一般的な構成だと、GitHubなどのソースコードリポジトリへのpushをトリガーとして、AWS CodeBuildのビルドステージを経由してデプロイしたりします。しかし、今回は単にECRリポジトリ内のコンテナイメージをそのままECRへデプロイしますので、ここはご認識いただければと思います。

1: イメージビルド時のプラットフォームに注意する

今回は、検証用ということでこのようなDockerfileを使用しました。

Dockerfile
FROM nginx:latest

このDockerfileを手元のPCでビルドして、ECRにpushすることになります。このビルドコマンドですが、通常のコマンドでビルドしたイメージをpushしたとします。

docker build -t aws-test-nginx-image .

今回の場合、このイメージをCodePipelineで参照させた時に、このようなデプロイログが繰り返し表示されて、デプロイが終了しない事象に遭遇しました。

format_error

デプロイが失敗するわけではないため、「デプロイ中...」の表示がずっと表示されたまま、一向にデプロイが完了しない状態になります。

このエラーでは、こちらのZenn記事を参考にしました。

https://zenn.dev/fdnsy/articles/a7be6e205a39d0

今回は、コンテナイメージビルド時に--platform linux/amd64のオプションをつけたことでデプロイが通るようになりました。手元のPCがMacだったために、AWS FargateのプラットフォームがMacと異なる状態になりエラーが発生したと思われます。

docker build --platform linux/amd64 -t aws-test-nginx-image .

コンテナイメージを手元でビルドしてからイメージリポジトリにpushする場合は、ビルド時のプラットフォームに気をつけましょう。

2: コンテナ定義ファイルを保存するS3バケット設定に注意する

ECRのコンテナイメージから直接CodePipeline経由でデプロイする際には、imagedefinitions.jsonというファイルを作成する必要があります。

https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html

imagedefinitions.jsonの内容は以下の通りです。nameにはコンテナ名、imageUriにはコンテナイメージのURIを記載しておきます。

imagedefinitions.json
[
    {
        "name": "nginx",
        "imageUri": "123456789.dkr.ecr.us-east-1.amazonaws.com/aws-test-ecr:latest"
    }
]

ECRから直接デプロイする際には、このimagedefinitions.jsonをzipで圧縮してS3バケットに保存する必要があります。このS3バケットですが、バージョニングを有効にしておく必要があります。バージョニングが有効になっていないS3バケットにimagedefinitions.json.zipを保存すると、デプロイ時にエラーになります。

ですので、imagedefinitions.jsonを保存するS3バケットはしっかりバージョニングを有効にしておきましょう。例として、Terraformで設定する場合は以下のようなコードになります。

#----------------------------------------------------------------#
# CodePipeline Artifacts Bucket
#----------------------------------------------------------------#

resource "aws_s3_bucket" "codepipeline_artifact" {
  bucket = local.codepipeline_artifact_bucket_name
}

resource "aws_s3_bucket_versioning" "codepipeline_artifact" {
  bucket = aws_s3_bucket.codepipeline_artifact.id
  versioning_configuration {
    status = "Enabled"
  }
}

3: ZIP圧縮時のパスに注意する

imagedefinitions.jsonファイルをS3にアップロードする際は、zipで圧縮する必要があります。

https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html

注記
ソースリポジトリが Amazon S3 バケットの場合は、JSON ファイルを圧縮してください。

このzip圧縮の際、以下のようなコマンドを使って、ディレクトリ構造ごとファイルを圧縮していました。

zip imagedefinitions.json.zip /Users/onoyan/works/aws-test-project/terraform/modules/pipeline/file/imagedefinitions.json

しかしこれだとimagedefinitions.json.zipを解凍すると、imagedefinitions.jsonではなく/Users/onoyan/works/aws-test-project/terraform/modules/pipeline/file/imagedefinitions.jsonが展開されます。不要なディレクトリ構造まで圧縮されてしまいます。

私の場合は、これが原因でCodePipelineの参照エラーになりました。

pipeline_execution_error

最新のアクション実行メッセージ
Did not find the image definition file imagedefinitions.json in the input artifacts ZIP file. Verify the file is stored in your pipeline's Amazon S3 artifact bucket: aws-test-s3-bucket key: aws-test-pipeline/source_out/XXXXXX.zip

そのため、S3にアップロードする際にはimagedefinitions.jsonがあるディレクトリ直下で実行するなどして、ディレクトリ構造まで圧縮しないようにしてください。

zip imagedefinitions.json.zip imagedefinitions.json

参考:CodePipelineのTerraformコード

以上のポイントに気をつけて、適切に設定できれば、以下のようなTerraformコードでコンテナイメージをデプロイできるようになります。

#----------------------------------------------------------------#
# CodePipeline
#----------------------------------------------------------------#

resource "aws_codepipeline" "aws_test_pipeline" {
  name     = "${var.system_name}-${var.environment}-pipeline"
  role_arn = aws_iam_role.codepipeline.arn

  artifact_store {
    type     = "S3"
    location = var.codepipeline_artifact_bucket
  }

  stage {
    name = "S3Setting"

    action {
      name             = "S3Setting"
      category         = "Source"
      owner            = "AWS"
      provider         = "S3"
      version          = "1"
      output_artifacts = ["source_output_s3"]

      configuration = {
        S3Bucket    = var.codepipeline_artifact_bucket
        S3ObjectKey = "imagedefinitions.json.zip"
      }
    }
  }

  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      input_artifacts = ["source_output_s3"]
      version         = "1"

      configuration = {
        ClusterName = var.cluster_name
        ServiceName = var.service_name_web
      }
    }
  }
}

CodePipelineに関するさまざまな設定に注意しよう

CodePipelineは、それ単体で動くことはほとんどないサービスです。今回では、S3やECR、ECSなどと連動していました。

そのため、CodePipelineだけでなく他のAWSサービスも含めて、注意深く設定するようにしましょう。では!

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.